// GraphWnd.cpp : implementation file
//

#include "stdafx.h"
#include <assert.h>
#include <math.h>
#include "wgnuplot.h"
#include "GraphWnd.h"

#include "src/bitmap.h"
#include "src/win/wcommon.h"
#include "src/term_api.h"
#include "src/getcolor.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

static unsigned char halftone_bitmaps[][16] =
{
	{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },   /// no fill
	{ 0xEE, 0xEE, 0xBB, 0xBB, 0xEE, 0xEE, 0xBB, 0xBB,
	0xEE, 0xEE, 0xBB, 0xBB, 0xEE, 0xEE, 0xBB, 0xBB },   /// 25% pattern
	{ 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55,
	0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55 },   /// 50% pattern
	{ 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
	0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22 },   /// 75% pattern
	{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }    /// solid pattern
};

#define halftone_num (sizeof(halftone_bitmaps) / sizeof (*halftone_bitmaps))
static HBRUSH halftone_brush[halftone_num];
static BITMAP halftone_bitdata[halftone_num];
static HBITMAP halftone_bitmap[halftone_num];

static unsigned char pattern_bitmaps[][16] = {
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* no fill */
  {0xFE, 0xFE, 0x7D, 0x7D, 0xBB, 0xBB, 0xD7, 0xD7,
   0xEF, 0xEF, 0xD7, 0xD7, 0xBB, 0xBB, 0x7D, 0x7D}, /* cross-hatch (1) */
  {0x77, 0x77, 0xBB, 0xBB, 0xDD, 0xDD, 0xBB, 0xBB,
   0x77, 0x77, 0xBB, 0xBB, 0xDD, 0xDD, 0xBB, 0xBB}, /* double cross-hatch (2) */
  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* solid fill (3) */
  {0xFE, 0xFE, 0xFD, 0xFD, 0xFB, 0xFB, 0xF7, 0xF7,
   0xEF, 0xEF, 0xDF, 0xDF, 0xBF, 0xBF, 0x7F, 0x7F}, /* diagonals (4) */
  {0x7F, 0x7F, 0xBF, 0xBF, 0xDF, 0xDF, 0xEF, 0xEF,
   0xF7, 0xF7, 0xFB, 0xFB, 0xFD, 0xFD, 0xFE, 0xFE}, /* diagonals (5) */
  {0xEE, 0xEE, 0xEE, 0xEE, 0xDD, 0xDD, 0xDD, 0xDD,
   0xBB, 0xBB, 0xBB, 0xBB, 0x77, 0x77, 0x77, 0x77}, /* steep diagonals (6) */
  {0x77, 0x77, 0x77, 0x77, 0xBB, 0xBB, 0xBB, 0xBB,
   0xDD, 0xDD, 0xDD, 0xDD, 0xEE, 0xEE, 0xEE, 0xEE}  /* steep diagonals (7) */
#if (0)
 ,{0xFC, 0xFC, 0xF3, 0xF3, 0xCF, 0xCF, 0x3F, 0x3F,
   0xFC, 0xFC, 0xF3, 0xF3, 0xCF, 0xCF, 0x3F, 0x3F}, /* shallow diagonals (old 5) */
  {0x3F, 0x3F, 0xCF, 0xCF, 0xF3, 0xF3, 0xFC, 0xFC,
   0x3F, 0x3F, 0xCF, 0xCF, 0xF3, 0xF3, 0xFC, 0xFC}  /* shallow diagonals (old 6) */
#endif
};
#define pattern_num (sizeof(pattern_bitmaps)/(sizeof(*pattern_bitmaps)))
static HBRUSH pattern_brush[pattern_num];
static BITMAP pattern_bitdata[pattern_num];
static HBITMAP pattern_bitmap[pattern_num];

static BOOL brushes_initialized = FALSE;

static HCURSOR hptrDefault, hptrCrossHair, hptrScaling, hptrRotating, hptrZooming, hptrCurrent;
#define GWOPMAX 4096
/////////////////////////////////////////////////////////////////////////////
// CGraphWnd

CGraphWnd::CGraphWnd()
{
}

CGraphWnd::~CGraphWnd()
{
}


BEGIN_MESSAGE_MAP(CGraphWnd, CWnd)
	//{{AFX_MSG_MAP(CGraphWnd)
	ON_WM_CREATE()
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CGraphWnd message handlers

int CGraphWnd::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	HDC hdc = ::GetDC( m_hWnd );
	MakePens(hdc);

	LoadCursors();

	RECT rect;
	GetClientRect( &rect );
	MakeFonts((LPRECT)&rect, hdc);
	::ReleaseDC(m_hWnd, hdc);

#if WINVER >= 0x030a
	{
		WORD version = LOWORD(GetVersion());
		
		if ((LOBYTE(version)*100 + HIBYTE(version)) >= 310)
			if ( lptw && (lptw->DragPre!=(LPSTR)NULL) && (lptw->DragPost!=(LPSTR)NULL) )
				DragAcceptFiles(TRUE);
	}
#endif
	
	return 0;
}

/* Prepare pens and brushes (--> colors) for use by the driver. Pens are (now) created
 * on-the-fly (--> DeleteObject(SelectObject(...)) idiom), but the brushes are still
 * all created statically, and kept until the window is closed */
void CGraphWnd::MakePens(HDC hdc)
{
	int i;
	
	if ((GetDeviceCaps(hdc,NUMCOLORS) == 2) || !color) 
	{
		hapen = CreatePenIndirect((LOGPEN FAR *)&monopen[1]); 	/// axis
		hbrush = CreateSolidBrush(RGB(255,255,255));
		for (i=0; i<WGNUMPENS+2; i++)
			colorbrush[i] = CreateSolidBrush(RGB(0,0,0));
	} else {
		hapen = CreatePenIndirect((LOGPEN FAR *)&colorpen[1]); 	/// axis
		hbrush = CreateSolidBrush(background);
		for (i=0; i<WGNUMPENS+2; i++)
			colorbrush[i] = CreateSolidBrush(colorpen[i].lopnColor);
	}
	
	/// build pattern brushes for filled boxes (ULIG)
	if( ! brushes_initialized ) 
	{
		int i;
		
		for(i=0; i < halftone_num; i++) 
		{
			halftone_bitdata[i].bmType       = 0;
			halftone_bitdata[i].bmWidth      = 16;
			halftone_bitdata[i].bmHeight     = 8;
			halftone_bitdata[i].bmWidthBytes = 2;
			halftone_bitdata[i].bmPlanes     = 1;
			halftone_bitdata[i].bmBitsPixel  = 1;
			halftone_bitdata[i].bmBits       = halftone_bitmaps[i];
			halftone_bitmap[i] = CreateBitmapIndirect(&halftone_bitdata[i]);
			halftone_brush[i] = CreatePatternBrush(halftone_bitmap[i]);
		}
		
		for(i=0; i < pattern_num; i++) 
		{
			pattern_bitdata[i].bmType       = 0;
			pattern_bitdata[i].bmWidth      = 16;
			pattern_bitdata[i].bmHeight     = 8;
			pattern_bitdata[i].bmWidthBytes = 2;
			pattern_bitdata[i].bmPlanes     = 1;
			pattern_bitdata[i].bmBitsPixel  = 1;
			pattern_bitdata[i].bmBits       = pattern_bitmaps[i];
			pattern_bitmap[i] = CreateBitmapIndirect(&pattern_bitdata[i]);
			pattern_brush[i] = CreatePatternBrush(pattern_bitmap[i]);
		}
		
		brushes_initialized = TRUE;
	}
}

void CGraphWnd::LoadCursors()
{
	///3 of them are standard cursor shapes:
	hptrDefault = LoadCursor(NULL, IDC_ARROW);
	hptrZooming = LoadCursor(NULL, IDC_SIZEALL);
	hptrCrossHair = LoadCursor( NULL, IDC_CROSS);
	///the other 2 are kept in the resource file:
	hptrScaling = LoadCursor( hInstance, MAKEINTRESOURCE(IDC_SCALING));
	hptrRotating = LoadCursor( hInstance, MAKEINTRESOURCE(IDC_ROTATING));
	
	hptrCurrent = hptrCrossHair;
}

void CGraphWnd::MakeFonts(LPRECT lprect, HDC hdc)
{
	HFONT hfontold;
	TEXTMETRIC tm;
	int result;
	char FAR *p;
	int cx, cy;
	
	rotate = FALSE;
	memset(&(lf), 0, sizeof(LOGFONT));
	strncpy(lf.lfFaceName,fontname,LF_FACESIZE);
	lf.lfHeight = -MulDiv(fontsize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
	lf.lfCharSet = DEFAULT_CHARSET;
	if ( (p = strstr(fontname," Italic")) != (LPSTR)NULL ) {
		lf.lfFaceName[ (unsigned int)(p-fontname) ] = '\0';
		lf.lfItalic = TRUE;
	}
	if ( (p = strstr(fontname," Bold")) != (LPSTR)NULL ) {
		lf.lfFaceName[ (unsigned int)(p-fontname) ] = '\0';
		lf.lfWeight = FW_BOLD;
	}
	
	if (hfonth == 0) {
		hfonth = CreateFontIndirect((LOGFONT FAR *)&(lf));
	}
	
	/// we do need a 90 degree font
	if (hfontv) 
		DeleteObject(hfontv);
	lf.lfEscapement = 900;
	lf.lfOrientation = 900;
	hfontv = CreateFontIndirect((LOGFONT FAR *)&(lf));
	
	/// save text size
	hfontold = (HFONT)SelectObject(hdc, hfonth);
	Wnd_GetTextSize(hdc, "0123456789", 10, &cx, &cy);
	vchar = MulDiv(cy,ymax,lprect->bottom - lprect->top);
	hchar = MulDiv(cx/10,xmax,lprect->right - lprect->left);
	
	/// CMW: Base tick size on character size
	htic = hchar / 2;
	cy = MulDiv(cx/20, GetDeviceCaps(hdc, LOGPIXELSY), GetDeviceCaps(hdc, LOGPIXELSX));
	vtic = MulDiv(cy,ymax,lprect->bottom - lprect->top);
	/// find out if we can rotate text 90deg
	SelectObject(hdc, hfontv);
	result = GetDeviceCaps(hdc, TEXTCAPS);
	if ((result & TC_CR_90) || (result & TC_CR_ANY))
		rotate = TRUE;
	GetTextMetrics(hdc,(TEXTMETRIC FAR *)&tm);
	if (tm.tmPitchAndFamily & TMPF_VECTOR)
		rotate = TRUE;	/// vector fonts can all be rotated
#if WINVER >=0x030a
	if (tm.tmPitchAndFamily & TMPF_TRUETYPE)
		rotate = TRUE;	/// truetype fonts can all be rotated
#endif
	SelectObject(hdc, hfontold);
	return;
}

void CGraphWnd::Wnd_GetTextSize(HDC hdc, LPCSTR str, size_t len, int *cx, int *cy)
{
#ifdef WIN32
	SIZE size;
	
	GetTextExtentPoint(hdc, str, len, &size);
	*cx = size.cx;
	*cy = size.cy;
#else
	/* Hmm... if not for compatibility to Win 3.0 and earlier, we could
	* use GetTextExtentPoint on Win16, too :-( */
	DWORD extent;
	
	extent = GetTextExtent(hdc, str, len);
	*cx = LOWORD(extent);
	*cy = HIWORD(extent);
#endif
}

void CGraphWnd::OnPaint() 
{
	CPaintDC dc(this); // device context for painting
	
	HDC hdc = dc.m_hDC;
	SetMapMode(hdc , MM_TEXT);
	SetBkMode(hdc , OPAQUE);
	
	RECT rect;
	GetClientRect(&rect);
#ifdef WIN32
	SetViewportExtEx(hdc, rect.right, rect.bottom, NULL);
#else
	SetViewportExt(hdc, rect.right, rect.bottom);
#endif
	drawgraph(hdc, &rect);

#ifdef USE_MOUSE
	/* drawgraph() erases the plot window, so immediately after
	* it has completed, all the 'real-time' stuff the gnuplot core
	* doesn't know anything about has to be redrawn */
	DrawRuler();
	//DisplayStatusLine(lpgw);
	//DrawRulerLineTo(lpgw);
	//DrawZoomBox(lpgw);
#endif
	// Do not call CWnd::OnPaint() for painting messages
}

void CGraphWnd::drawgraph(HDC hdc, LPRECT rect)
{
	int xdash, ydash;			/* the transformed coordinates */
	int rr, rl, rt, rb;
	struct GWOP FAR *curptr;
	struct GWOPBLK *blkptr;
	int htic, vtic;
	int hshift, vshift;
	unsigned int lastop=-1;		/* used for plotting last point on a line */
	int pen;
	int polymax = 200;
	int polyi = 0;
	POINT *ppt;
	unsigned int ngwop=0;
	BOOL isColor;
	double line_width = 1.0;
	unsigned int fillstyle = 0.0;
	int idx;
	
	if (locked)
		return;
	
		/* HBB 20010218: the GDI status query functions don't work on Metafile
		* handles, so can't know whether the screen is actually showing
		* color or not, if drawgraph() is being called from CopyClip().
	* Solve by defaulting isColor to 1 if hdc is a metafile. */
	isColor = (((GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc,BITSPIXEL))
		> 2)
		|| (GetDeviceCaps(hdc, TECHNOLOGY) == DT_METAFILE));
	
	if (background != RGB(255,255,255) && color && isColor) {
		SetBkColor(hdc,background);
		FillRect(hdc, rect, hbrush);
	}
	
	ppt = (POINT *)LocalAllocPtr(LHND, (polymax+1) * sizeof(POINT));
	
	rr = rect->right;
	rl = rect->left;
	rt = rect->top;
	rb = rect->bottom;
	
	htic = org_pointsize*MulDiv(htic, rr - rl, xmax) + 1;
	vtic = org_pointsize*MulDiv(vtic, rb - rt, ymax) + 1;
	
	angle = 0;
	SetFont(hdc);
	SetTextAlign(hdc, TA_LEFT|TA_BASELINE);
	
	/* calculate text shifting for horizontal text */
	hshift = 0;
	vshift = MulDiv(vchar, rb - rt, ymax)/2;
	
	pen = 2;
	SelectObject(hdc, hapen);
	SelectObject(hdc, colorbrush[pen]);
	
	/* do the drawing */
	blkptr = gwopblk_head;
	curptr = NULL;
	if (blkptr) {
		if (!blkptr->gwop)
			blkptr->gwop = (struct GWOP FAR *)GlobalLock(blkptr->hblk);
		if (!blkptr->gwop)
			return;
		curptr = (struct GWOP FAR *)blkptr->gwop;
	}
	
	while(ngwop < nGWOP) {
		/* transform the coordinates */
		xdash = MulDiv(curptr->x, rr-rl-1, xmax) + rl;
		ydash = MulDiv(curptr->y, rt-rb+1, ymax) + rb - 1;
		if ((lastop==W_vect) && (curptr->op!=W_vect)) {
			if (polyi >= 2)
				Polyline(hdc, ppt, polyi);
			polyi = 0;
		}
		switch (curptr->op) {
		case 0:	/* have run past last in this block */
			break;
		case W_move:
			ppt[0].x = xdash;
			ppt[0].y = ydash;
			polyi = 1;
			break;
		case W_vect:
			ppt[polyi].x = xdash;
			ppt[polyi].y = ydash;
			polyi++;
			if (polyi >= polymax) {
				Polyline(hdc, ppt, polyi);
				ppt[0].x = xdash;
				ppt[0].y = ydash;
				polyi = 1;
			}
			break;
		case W_line_type:
			{
				short cur_pen = ((curptr->x < (WORD)(-2))
					? (curptr->x % WGNUMPENS) + 2
					: curptr->x + 2);
				LOGPEN cur_penstruct = (color && isColor) ?
					colorpen[cur_pen] : monopen[cur_pen];
				
				if (line_width != 1)
					cur_penstruct.lopnWidth.x *= line_width;
				hapen = CreatePenIndirect((LOGPEN FAR *) &cur_penstruct);
				DeleteObject(SelectObject(hdc, hapen));
				
				pen = cur_pen;
				SelectObject(hdc, colorbrush[pen]);
				/* PM 7.7.2002: support color text */
				SetTextColor(hdc, colorpen[pen].lopnColor);
			}
			break;
			
		case W_put_text:
			{
				char *str;
				str = (char *)LocalLock(curptr->htext);
				if (str) {
					/* shift correctly for rotated text */
					xdash += hshift;
					ydash += vshift;
					
					SetBkMode(hdc,TRANSPARENT);
					TextOut(hdc,xdash,ydash,str,lstrlen(str));
					SetBkMode(hdc,OPAQUE);
				}
				LocalUnlock(curptr->htext);
			}
			break;
			
		case W_fillstyle:
		/* HBB 20010916: new entry, needed to squeeze the many
		* parameters of a filled box call through the bottlneck
		* of the fixed number of parameters in GraphOp() and
			* struct GWOP, respectively. */
			fillstyle = curptr->x;
			break;
			
		case W_boxfill:   /* ULIG */
			
			assert (polyi == 1);
			
			/* NOTE: the x and y passed with this call are the width and
			* height of the box, actually. The left corner was stored into
			* ppt[0] by a preceding W_move, and the style was memorized
			* by a W_fillstyle call. */
			switch(fillstyle & 0x0f) {
			case FS_SOLID:
			case FS_TRANSPARENT_SOLID:
			/* style == 1 --> use halftone fill pattern
			* according to filldensity. Density is from
				* 0..100 percent: */
				idx = ((fillstyle >> 4) * (halftone_num - 1) / 100 );
				if (idx < 0)
					idx = 0;
				if (idx > halftone_num - 1)
					idx = halftone_num - 1;
				SelectObject(hdc, halftone_brush[idx]);
				break;
			case FS_PATTERN:
			case FS_TRANSPARENT_PATTERN:
			/* style == 2 --> use fill pattern according to
				* fillpattern. Pattern number is enumerated */
				idx = fillstyle >> 4;
				if (idx < 0)
					idx = 0;
				if (idx > pattern_num - 1)
					idx = 0;
				SelectObject(hdc, pattern_brush[idx]);
				break;
			case FS_EMPTY:
			default:
				/* style == 0 or unknown --> fill with background color */
				SelectObject(hdc, halftone_brush[0]);
			}
			/* needs to be fixed for monochrome devices */
			/* FIXME: probably should keep track of text color */
			SetTextColor(hdc, colorpen[pen].lopnColor);
			xdash -= rl;
			ydash -= rb - 1;
			PatBlt(hdc, ppt[0].x, ppt[0].y, xdash, ydash, PATCOPY);
			polyi = 0;
			break;
			case W_text_angle:
				if (angle != (short int)curptr->x) 
				{
					angle = (short int)curptr->x;
					/* correctly calculate shifting of rotated text */
					hshift = sin(M_PI/180. * angle) * MulDiv(vchar, rr-rl, xmax) / 2;
					vshift = cos(M_PI/180. * angle) * MulDiv(vchar, rb-rt, ymax) / 2;
					SetFont(hdc);
				}
				break;
			case W_justify:
				switch (curptr->x)
				{
				case LEFT:
					SetTextAlign(hdc, TA_LEFT|TA_BASELINE);
					break;
				case RIGHT:
					SetTextAlign(hdc, TA_RIGHT|TA_BASELINE);
					break;
				case CENTRE:
					SetTextAlign(hdc, TA_CENTER|TA_BASELINE);
					break;
				}
				break;
				case W_font: {
					char *font;
					
					font = (char *)LocalLock(curptr->htext);
					if (font) {
						GraphChangeFont(font, curptr->x, hdc, *rect);
						SetFont(hdc);
					}
					LocalUnlock(curptr->htext);
					     }
					break;
				case W_pointsize:
					if (curptr->x != 0) {
						double pointsize = curptr->x / 100.0;
						htic = pointsize*MulDiv(htic, rr-rl, xmax) + 1;
						vtic = pointsize*MulDiv(vtic, rb-rt, ymax) + 1;
					} else {
						char *str;
						str = (char *)LocalLock(curptr->htext);
						if (str) {
							double pointsize;
							sscanf(str, "%lg", &pointsize);
							htic = org_pointsize
								* MulDiv(htic, rr-rl, xmax) + 1;
							vtic = org_pointsize
								* MulDiv(vtic, rb-rt, ymax) + 1;
						}
						LocalUnlock(curptr->htext);
					}
					break;
				case W_line_width:
				/* HBB 20000813: this may look strange, but it ensures
				* that linewidth is exactly 1 iff it's in default
					* state */
					line_width = curptr->x == 100 ? 1 : (curptr->x / 100.0);
					break;
				case W_pm3d_setcolor:
					{
						static HBRUSH last_pm3d_brush = NULL;
						HBRUSH this_brush;
						COLORREF c;
						
						/* distinguish gray values and RGB colors */
						if (curptr->y == 0) 
						{
							rgb255_color rgb255;
							rgb255maxcolors_from_gray( curptr->x / 256.0, &rgb255 );
							c = RGB(rgb255.r, rgb255.g, rgb255.b);
						}
						else {
							c = RGB(curptr->y & 0xff, (curptr->x >> 8) & 0xff, curptr->x & 0xff);
						}
						
						this_brush = CreateSolidBrush(c);
						SelectObject(hdc, this_brush);
						if (last_pm3d_brush != NULL)
							DeleteObject(last_pm3d_brush);
						last_pm3d_brush = this_brush;
						/* create new pen, too: */
						DeleteObject(SelectObject(hdc, CreatePen(PS_SOLID, line_width, c)));
						/* finally set text color */
						SetTextColor(hdc, c);
					}
					break;
				case W_pm3d_filled_polygon_pt:
					{
						/* a point of the polygon is coming */
						if (polyi >= polymax) {
							polymax += 200;
							ppt = (POINT *)LocalReAllocPtr(ppt, LHND, (polymax+1) * sizeof(POINT));
						}
						ppt[polyi].x = xdash;
						ppt[polyi].y = ydash;
						polyi++;
					}
					break;
				case W_pm3d_filled_polygon_draw:
					{
						/* end of point series --> draw polygon now */
						Polygon(hdc, ppt, polyi);
						polyi = 0;
					}
					break;
				case W_image:
					{
					/* Due to the structure of gwop in total 5 entries are needed.
					These static variables help to collect all the needed information
						*/
						static int seq = 0;  /* sequence counter */
						static POINT corners[4];
						
						if (seq < 4) {
							/* The first four OPs contain the `corner` array */
							corners[seq].x = xdash;
							corners[seq].y = ydash;
						} else {
							/* The last OP contains the image and it's size */
							BITMAPINFO bmi;
							char *image;
							unsigned int M, N;
							int rc;
							
							M = curptr->x;
							N = curptr->y;
							image = (char *)LocalLock(curptr->htext);
							if (image) {
								
								/* rc = SetStretchBltMode(hdc, HALFTONE); */
								
								memset(&bmi, 0, sizeof(bmi));
								bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
								bmi.bmiHeader.biWidth = M;
								bmi.bmiHeader.biHeight = N;
								bmi.bmiHeader.biPlanes = 1;
								bmi.bmiHeader.biBitCount = 24;
								bmi.bmiHeader.biCompression = BI_RGB;
								bmi.bmiHeader.biClrUsed = 0;
								
								rc = StretchDIBits(hdc, 
									corners[0].x, corners[0].y, 
									corners[1].x - corners[0].x, corners[1].y - corners[0].y,
									0, 0,
									M, N,
									image, &bmi,
									DIB_RGB_COLORS, SRCCOPY );
							}
							LocalUnlock(curptr->htext);
						}
						seq = (seq + 1) % 5;
					}
					break;
				case W_dot:
					dot(hdc, xdash, ydash);
					break;
				case W_plus: /* do plus */
					MoveTo(hdc,xdash-htic,ydash);
					LineTo(hdc,xdash+htic+1,ydash);
					MoveTo(hdc,xdash,ydash-vtic);
					LineTo(hdc,xdash,ydash+vtic+1);
					break;
				case W_cross: /* do X */
					MoveTo(hdc,xdash-htic,ydash-vtic);
					LineTo(hdc,xdash+htic+1,ydash+vtic+1);
					MoveTo(hdc,xdash-htic,ydash+vtic);
					LineTo(hdc,xdash+htic+1,ydash-vtic-1);
					break;
				case W_star: /* do star */
					MoveTo(hdc,xdash-htic,ydash);
					LineTo(hdc,xdash+htic+1,ydash);
					MoveTo(hdc,xdash,ydash-vtic);
					LineTo(hdc,xdash,ydash+vtic+1);
					MoveTo(hdc,xdash-htic,ydash-vtic);
					LineTo(hdc,xdash+htic+1,ydash+vtic+1);
					MoveTo(hdc,xdash-htic,ydash+vtic);
					LineTo(hdc,xdash+htic+1,ydash-vtic-1);
					break;
				case W_circle: /* do open circle */
					Arc(hdc, xdash-htic, ydash-vtic, xdash+htic+1, ydash+vtic+1,
						xdash, ydash+vtic+1, xdash, ydash+vtic+1);
					dot(hdc, xdash, ydash);
					break;
				case W_fcircle: /* do filled circle */
					Ellipse(hdc, xdash-htic, ydash-vtic,
						xdash+htic+1, ydash+vtic+1);
					break;
				default:	/* potentially closed figure */
					{
						POINT p[6];
						int i;
						int shape = 0;
						int filled = 0;
						static float pointshapes[5][10] = {
							{-1, -1, +1, -1, +1, +1, -1, +1, 0, 0}, /* box */
							{ 0, +1, -1,  0,  0, -1, +1,  0, 0, 0}, /* diamond */
							{ 0, -4./3, -4./3, 2./3,
							4./3,  2./3, 0, 0}, /* triangle */
							{ 0, 4./3, -4./3, -2./3,
							4./3,  -2./3, 0, 0}, /* inverted triangle */
							{ 0, 1, 0.95106, 0.30902, 0.58779, -0.80902,
							-0.58779, -0.80902, -0.95106, 0.30902} /* pentagon */
						};
						
						switch (curptr->op) {
						case W_box:
							shape = 0;
							break;
						case W_diamond:
							shape = 1;
							break;
						case W_itriangle:
							shape = 2;
							break;
						case W_triangle:
							shape = 3;
							break;
						case W_pentagon:
							shape = 4;
							break;
						case W_fbox:
							shape = 0;
							filled = 1;
							break;
						case W_fdiamond:
							shape = 1;
							filled = 1;
							break;
						case W_fitriangle:
							shape = 2;
							filled = 1;
							break;
						case W_ftriangle:
							shape = 3;
							filled = 1;
							break;
						case W_fpentagon:
							shape = 4;
							filled = 1;
							break;
						}
						
						for (i = 0; i < 5; ++i) {
							if (pointshapes[shape][i * 2 + 1] == 0
								&& pointshapes[shape][i * 2] == 0)
								break;
							p[i].x = xdash + htic*pointshapes[shape][i*2] + 0.5;
							p[i].y = ydash + vtic*pointshapes[shape][i*2+1] + 0.5;
						}
						if ( filled )
							/* Filled polygon */
							Polygon(hdc, p, i);
						else {
							/* Outline polygon */
							p[i].x = p[0].x;
							p[i].y = p[0].y;
							Polyline(hdc, p, i+1);
							dot(hdc, xdash, ydash);
						}
					} /* default case */
	} /* switch(opcode) */
	lastop = curptr->op;
	ngwop++;
	curptr++;
	if ((unsigned)(curptr - blkptr->gwop) >= GWOPMAX) {
		GlobalUnlock(blkptr->hblk);
		blkptr->gwop = (struct GWOP FAR *)NULL;
		if ((blkptr = blkptr->next) == NULL)
		/* If exact multiple of GWOPMAX entries are queued,
		* next will be NULL. Only the next GraphOp() call would
		* have allocated a new block */
		return;
		if (!blkptr->gwop)
			blkptr->gwop = (struct GWOP FAR *)GlobalLock(blkptr->hblk);
		if (!blkptr->gwop)
			return;
		curptr = (struct GWOP FAR *)blkptr->gwop;
	}
    }
    if (polyi >= 2)
	    Polyline(hdc, ppt, polyi);
    LocalFreePtr(ppt);
}

void CGraphWnd::SetFont(HDC hdc)
{
	if (rotate && angle) 
	{
		if (hfontv)
			DeleteObject(hfontv);
		lf.lfEscapement = lf.lfOrientation  = angle * 10;
		hfontv = CreateFontIndirect((LOGFONT FAR *)&(lf));
		if (hfontv)
			SelectObject(hdc, hfontv);
	} else {
		if (hfonth)
			SelectObject(hdc, hfonth);
	}
	return;
}

void CGraphWnd::GraphChangeFont(LPCSTR font, int fontsize, HDC hdc, RECT rect)
{
	int newfontsize;
	bool remakefonts = FALSE;
	
	newfontsize = (fontsize != 0) ? fontsize : deffontsize;
	if (font != NULL) {
		remakefonts = (strcmp(fontname, font) != 0) || (newfontsize != fontsize);
	} else {
		remakefonts = (strcmp(fontname, deffontname) != 0) || (newfontsize != fontsize);
	}
	
	if (remakefonts) 
	{
		fontsize = newfontsize;
		strcpy(fontname, (font) ? font : deffontname);
		
		DestroyFonts();
		MakeFonts(&rect, hdc);
	}
}

void CGraphWnd::dot(HDC hdc, int xdash, int ydash)
{
	MoveTo(hdc, xdash, ydash);
	LineTo(hdc, xdash, ydash+1);
}

void CGraphWnd::DestroyFonts()
{
	if (hfonth) {
		DeleteObject(hfonth);
		hfonth = 0;
	}
	if (hfontv) {
		DeleteObject(hfontv);
		hfontv = 0;
	}
	return;
}

void CGraphWnd::rgb255maxcolors_from_gray(double gray, rgb255_color *rgb255)
{
	rgb_color rgb1;
	rgb1maxcolors_from_gray(gray, &rgb1);
	rgb255_from_rgb1(rgb1, rgb255);
}

void CGraphWnd::rgb1maxcolors_from_gray(double gray, rgb_color *color)
{
	if (sm_palette.use_maxcolors != 0)
		gray = floor(gray * sm_palette.use_maxcolors)
		/ (sm_palette.use_maxcolors-1);
	rgb1_from_gray(gray, color);
}

void CGraphWnd::rgb255_from_rgb1(rgb_color rgb1, rgb255_color *rgb255)
{
	rgb255->r = (unsigned char)(255 * rgb1.r + 0.5);
	rgb255->g = (unsigned char)(255 * rgb1.g + 0.5);
	rgb255->b = (unsigned char)(255 * rgb1.b + 0.5);
}

void CGraphWnd::rgb1_from_gray(double gray, rgb_color *color)
{
	/* get the color */
	color_components_from_gray(gray, color);
	if (sm_palette.colorMode == SMPAL_COLOR_MODE_GRAY)
		return;
	
	/* transform to RGB if necessary */
	switch(sm_palette.cmodel) 
	{
		case C_MODEL_RGB:
			break;
		case C_MODEL_HSV:
			HSV_2_RGB(color);
			break;
		case C_MODEL_CMY:
			CMY_2_RGB(color);
			break;
		case C_MODEL_YIQ:
			YIQ_2_RGB(color);
			break;
		case C_MODEL_XYZ:
			CIEXYZ_2_RGB(color);
			break;
		default:
			fprintf(stderr, "%s:%d ooops: Unknown color model '%c'\n",
				__FILE__, __LINE__, (char)(sm_palette.cmodel));
	}
}

void CGraphWnd::color_components_from_gray(double gray, rgb_color *color)
{
	if (gray < 0)
		gray = 0;
	else if (gray > 1.0)
		gray = 1.0;
	
	switch(sm_palette.colorMode) 
	{
		case SMPAL_COLOR_MODE_GRAY:
			color->r = color->g = color->b = pow(gray, 1.0/sm_palette.gamma);
			return;  /* all done, no color space transformation needed  */
		case SMPAL_COLOR_MODE_RGB:
			color->r = GetColorValueFromFormula(sm_palette.formulaR, gray);
			color->g = GetColorValueFromFormula(sm_palette.formulaG, gray);
			color->b = GetColorValueFromFormula(sm_palette.formulaB, gray);
			break;
		case SMPAL_COLOR_MODE_GRADIENT:
			interpolate_color_from_gray(gray, color);
			break;
	#ifndef GPLT_X11_MODE
		case SMPAL_COLOR_MODE_FUNCTIONS:
			calculate_color_from_formulae(gray, color);
			break;
	#endif /* !GPLT_X11_MODE */
		default:
			fprintf(stderr, "%s:%d ooops: Unknown colorMode '%c'.\n",
				__FILE__, __LINE__, (char)(sm_palette.colorMode));
	}
}

void CGraphWnd::DrawRuler()
{
	/*
	HDC hdc;
	int iOldRop;
	RECT rc;
	long rx, ry;
	
	if (!ruler.on || ruler.x < 0) return;
	
	hdc = GetDC();
	GetClientRect(hWndGraph, &rc);
	
	rx = MulDiv(ruler.x, rc.right - rc.left, xmax);
	ry = rc.bottom - MulDiv(ruler.y, rc.bottom - rc.top, ymax);
	
	iOldRop = SetROP2(hdc, R2_NOT);
	MoveTo(hdc, rc.left, ry);
	LineTo(hdc, rc.right, ry);
	MoveTo(hdc, rx, rc.top);
	LineTo(hdc, rx, rc.bottom);
	SetROP2(hdc, iOldRop);
	ReleaseDC(hWndGraph, hdc);
	*/
}
